﻿// Copyright (c)  MOKEYISH. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Text;
using NSQL.Expressions;
using NSQL.Expressions.Functions;
using NSQL.Generator.Formatters;
using NSQL.LogicalResource;
using NSQL.Statements;

namespace NSQL.Generator
{
    public class OracleGenerator : BaseGenerator, IExpressionVisitor<StringBuilder>
    {
        protected string Offset => Format(nameof(Offset));
        protected string Row => Format(nameof(Row));
        protected string Rows => Format(nameof(Rows));
        protected string Fetch => Format(nameof(Fetch));
        protected string First => Format(nameof(First));
        protected string Only => Format(nameof(Only));
        protected string Nvl2 => Format(nameof(Nvl2));
        protected string Dual => Format(nameof(Dual));
        protected override string PadLeft => Format("LPad");
        protected override string PadRight => Format("RPad");
        protected override string TrimStart => Format("LTrim");
        protected override string TrimEnd => Format("RTrim");
        protected override string Now => Format("SysDate");
        
        StringBuilder IExpressionVisitor<StringBuilder>.Visit(IColumnExpression columnExpression)
        {
            var builder = new StringBuilder();
            var columnName = columnExpression.Name;
            if (columnExpression.Id > 0)
            {
                var column = LogicalDatabase?.FindColumnById(columnExpression.Id);
                if (column != null)
                {
                    columnName = column.Name;
                }
            }
            
            if (columnExpression.Delimited)
            {
                builder.Append('"');
                builder.Append(columnName);
                builder.Append('"');
            }
            else
            {
                builder.Append(columnName);
            }
            return builder;
        }

        protected override StringBuilder FormatSelect(StringBuilder select, StringBuilder from, StringBuilder where, StringBuilder group,
            StringBuilder order, StringBuilder having, int skip, int limit)
        {
            var builder = FormatSelect(select, from, where, group, order, having);

            if (skip > 0)
            {
                builder.Append(' ').Append(Offset);
                builder.Append(' ').Append(skip);
                builder.Append(' ').Append(skip > 1 ? Rows : Row);
            }

            if (limit <= 0) return builder;

            builder.Append(' ').Append(Fetch);
            builder.Append(' ').Append(First);
            builder.Append(' ').Append(limit);
            builder.Append(' ').Append(limit > 1 ? Rows : Row);
            builder.Append(' ').Append(Only);
            
            return builder;
        }
        protected override StringBuilder FormatSelect(StringBuilder select, StringBuilder from, StringBuilder where, StringBuilder group,
            StringBuilder order, StringBuilder having)
        {
            if (!(from?.Length > 0))
            {
                from = new StringBuilder(Dual);
            }
            return base.FormatSelect(select, from, where, group, order, having);
        }
        
        StringBuilder IFunctionVisitor<StringBuilder>.Visit(ILeftFunction leftFunction)
        {
            return new SubStringFunction(leftFunction.Text, (IntegerExpression) 1, leftFunction.Length).Accept(this);
        }
        StringBuilder IFunctionVisitor<StringBuilder>.Visit(IRightFunction rightFunction)
        {
            return new SubStringFunction(rightFunction.Text, new LengthFunction(rightFunction.Text) - rightFunction.Length + 1, rightFunction.Length).Accept(this);
        }
        
        StringBuilder IFunctionVisitor<StringBuilder>.Visit(INvlFunction nvlFunction)
        {
            var builder = new StringBuilder();
            var hasThen = !(nvlFunction.Then is null);

            builder.Append(hasThen ? Nvl2 : Nvl);
            
            builder.Append('(');
            builder.Append(nvlFunction.Expr.Accept(this));
            if (hasThen)
            {
                builder.Append(", ").Append(nvlFunction.Then.Accept(this));
            }
            builder.Append(", ").Append(nvlFunction.Default.Accept(this));
            builder.Append(')');
            return builder;
        }

        public OracleGenerator(SqlGeneratorSettings settings) : base(settings)
        {
        }
    }
    
        
    public static class OracleGeneratorExtensions
    {
        public static string ToOracle(this IStatement statement, SqlGeneratorSettings settings = null)
        {
            return new OracleGenerator(settings ?? new SqlGeneratorSettings
            {
                KeywordStyle = KeywordStyle.UpperCase
            }).Generate(statement);
        }
        public static string ToOracle(this IStatement statement, ILogicalDatabase database)
        {
            return new OracleGenerator(new SqlGeneratorSettings
            {
                Database = database,
                KeywordStyle = KeywordStyle.UpperCase
            }).Generate(statement);
        }
    }
}